package com.opengw.configuration.support;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import javax.wsdl.Definition;
import javax.wsdl.Message;
import javax.wsdl.Port;
import javax.wsdl.Service;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.xml.namespace.QName;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.ibm.wsdl.extensions.http.HTTPAddressImpl;
import com.ibm.wsdl.extensions.schema.SchemaImpl;
import com.ibm.wsdl.extensions.schema.SchemaImportImpl;
import com.ibm.wsdl.extensions.soap.SOAPAddressImpl;
import com.ibm.wsdl.extensions.soap12.SOAP12AddressImpl;
import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class WSDLInitializer {
	
    private static final String W3C_WSDL_SCHEMA = "http://schemas.xmlsoap.org/wsdl/";
    private static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
	
	private Definition definition = null;
	private HashMap<String, String[]> xsdImports = null;
	private String endPointURL = null;
	
	public WSDLInitializer(String url) throws WSDLException{
		WSDLFactory factory = WSDLFactory.newInstance();
        WSDLReader reader = factory.newWSDLReader();
        reader.setFeature("javax.wsdl.verbose", true);
        reader.setFeature("javax.wsdl.importDocuments", true);
        this.definition = reader.readWSDL(null, url);
        this.xsdImports = new HashMap<String, String[]>();
		Types types = this.definition.getTypes();
		for (Object tmpObj : types.getExtensibilityElements()) {
            if(tmpObj instanceof SchemaImpl){
                SchemaImpl schemaImp = (SchemaImpl) tmpObj;
                Map imports = schemaImp.getImports();
                Iterator schemaIte = imports.keySet().iterator();
                while(schemaIte.hasNext()){
                    String schemaKey = (String) schemaIte.next();
                    Vector<SchemaImportImpl> importSchemas = (Vector) imports.get(schemaKey);
                    for(SchemaImportImpl schemaImport : importSchemas){
                    	String[] schemaArr = new String[2];
                    	schemaArr[0] = schemaImport.getSchemaLocationURI();
                    	this.xsdImports.put(schemaKey, schemaArr);
                    }
                }
            }
        }
	}
	
	public Types getWSDLTypes(){
		return this.definition.getTypes();
	}
	
	public Map getMessages(){
		return this.definition.getMessages();
	}
		
	public void changeXSDImport(String namespace, String schemaLocationURI) throws Exception{

		String[] schemaArr = this.xsdImports.get(namespace);
		
		if(schemaArr != null){
			schemaArr[1] = schemaLocationURI;
		} else {
			throw new Exception();
		}
	}
	
	public void clearXSDImportChanges(){
		Set<String> keySet = this.xsdImports.keySet();
		Iterator<String> keyIterator = keySet.iterator();
		while(keyIterator.hasNext()){
			String key = keyIterator.next();
			String[] schemaArr = xsdImports.get(key);
			schemaArr[1] = null;
		}
	}
	
	public byte[] toByteArray() throws Exception{
		
		changeEndPointLocationURI();
		
		WSDLFactory factory = WSDLFactory.newInstance();
		Document doc = factory.newWSDLWriter().getDocument(this.definition);
		
		//TODO Şimdilik kapalı
//		changeSchemaImports(doc);
		
		OutputFormat format = new OutputFormat(doc);
        format.setIndenting(true);
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        XMLSerializer serializer = new XMLSerializer(outStream, format);
        serializer.serialize(doc);
        
        return outStream.toByteArray();
	}
	
	private void changeEndPointLocationURI(){
		Map services = this.definition.getServices();
        Iterator iterator = services.keySet().iterator();
        while (iterator.hasNext()) {
            QName key = (QName) iterator.next();
            Service service = (Service) services.get(key);
            Map ports = service.getPorts();
            Iterator portIterator = service.getPorts().keySet().iterator();
            while (portIterator.hasNext()) {
                String subkey = (String) portIterator.next();
                Port port = (Port) ports.get(subkey);

                for (Object obj : port.getExtensibilityElements()) {
                    if (obj instanceof SOAPAddressImpl) {
                        SOAPAddressImpl soap = (SOAPAddressImpl) obj;
                        soap.setLocationURI(this.endPointURL);
                    } else if (obj instanceof SOAP12AddressImpl) {
                        SOAP12AddressImpl soap = (SOAP12AddressImpl) obj;
                        soap.setLocationURI(this.endPointURL);
                    } else if (obj instanceof HTTPAddressImpl) {
                        HTTPAddressImpl http = (HTTPAddressImpl) obj;
                        http.setLocationURI(this.endPointURL);
                    } 
                }

            }

        }
	}
	
	private void changeSchemaImports(Document document) throws Exception{
		String prefix = createPrefix(document.lookupPrefix(W3C_WSDL_SCHEMA));
        String xsdPre = createPrefix(document.lookupPrefix(W3C_XML_SCHEMA));
        NodeList childeren = document.getChildNodes();
        Node rootNode = null;
        for (int i = 0; i < childeren.getLength(); i++) {
            Node node = childeren.item(i);
            if (node.getNodeName().equals(prefix + "definitions")) {
                rootNode = node;
                break;
            }
        }

        if (rootNode == null) {
            throw new Exception("DEFINITIONS CAN NOT FIND");
        }

        childeren = rootNode.getChildNodes();
        Node typeNode = null;

        for (int i = 0; i < childeren.getLength(); i++) {
            Node node = childeren.item(i);
            if (node.getNodeName().equals(prefix + "types")) {
                typeNode = node;
                break;
            }
        }

        childeren = typeNode.getChildNodes();
        List<Node> schemaNodes = new ArrayList<Node>();
        for (int i = 0; i < childeren.getLength(); i++) {
            Node node = childeren.item(i);
            if (node.getNodeName().equals(xsdPre + "schema")) {
                schemaNodes.add(node);
            }
        }

        for(Node schemaNode : schemaNodes){
            childeren = schemaNode.getChildNodes();
            List<Node> importNodes = new ArrayList<Node>();
            for (int i = 0; i < childeren.getLength(); i++) {
                Node node = childeren.item(i);
                if (node.getNodeName().equals(xsdPre + "import")) {
                    importNodes.add(node);
                }
            }

            for(Node importNode : importNodes){
                NamedNodeMap atts = importNode.getAttributes();
                Node nameSpaceNode = null;
                Node schemaLocationNode = null;
                for(int j = 0; j < atts.getLength(); j++){
                    Node att = atts.item(j);
                    if(att.getLocalName().equals("namespace")){
                    	nameSpaceNode = att;
                    }
                    if(att.getLocalName().equals("schemaLocation")){                        
                        schemaLocationNode = att;
                    }
                }
                
                String[] schemaArr = this.xsdImports.get(nameSpaceNode.getNodeValue());
                if(schemaArr[1] != null){
                	schemaLocationNode.setNodeValue(schemaArr[1]);
                }
            }
        }
	}
	
	public void setEndPointURL(String endPointURL) {
		this.endPointURL = endPointURL;
	}

	public String getEndPointURL() {
		return endPointURL;
	}
	
    private String createPrefix(String prefix) {
        if (prefix == null) {
            prefix = "";
        } else {
            prefix += ":";
        }

        return prefix;
    }
	
	
	
}
